home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Toolbox / EditTextCdev / EditCdev.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-18  |  9.5 KB  |  284 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------
  2. #
  3. #    Macintosh Developer Technical Support
  4. #
  5. #    EditText Sample Control Panel Device
  6. #
  7. #    EditCdev
  8. #
  9. #    EditCdev.c    -    C Source
  10. #
  11. #    Copyright © 1988 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:    1.00                8/88
  15. #                1.10                6/92
  16. #
  17. #    Components:    EditCdev.p            August 1, 1988
  18. #                EditCdev.c            August 1, 1988
  19. #                EditCdev.r            August 1, 1988
  20. #                PEditCdev.make        August 1, 1988
  21. #                CEditCdev.make        August 1, 1988
  22. #                TCEditCdev.π        June 12, 1992
  23. #                TCEditCdev.π.rsrc    June 12, 1992
  24. #
  25. #    EditCdev is a sample Control Panel device (cdev) that 
  26. #    demonstrates the usage of the edit-related messages.  
  27. #    EditCdev demonstrates how to implement an editText item
  28. #    in a Control Panel Device.  It utilizes the new undo, cut, copy,
  29. #    paste, and delete messages that are sent to cdevs in
  30. #    response to user menu selections.
  31. #
  32. #    It is comprised of two editText items that can be edited 
  33. #    and moved between via the mouse or tab key.
  34. #
  35. ------------------------------------------------------------------------------*/
  36.  
  37. #include <Types.h>
  38. #include <Memory.h>
  39. #include <Quickdraw.h>
  40. #include <TextEdit.h>
  41. #include <Dialogs.h>
  42. #include <Events.h>
  43. #include <Devices.h>
  44. #include <Scrap.h>
  45. #include <Packages.h>
  46. #include <TextEdit.h>
  47. #include <TextUtils.h>
  48.  
  49. /* Constants */
  50. #define    textItm        1            /* first editText item in cdev */
  51. #define countItm    4            /* first staticText item for displaying character count */
  52.  
  53. #define    undoDev        9            /* cdev edit messages */
  54. #define    cutDev        10
  55. #define    copyDev        11
  56. #define    pasteDev    12
  57. #define    clearDev    13
  58.  
  59. #define maxLength    50            /* maximum # of characters in each textEdit field < 32K */
  60.  
  61. #define delete        0x08        /* character codes for various keys */
  62. #define tab            0x09
  63. #define leftArrow    0x1c
  64. #define rightArrow    0x1d
  65. #define upArrow        0x1e
  66. #define downArrow    0x1f
  67.  
  68. /*
  69.  *  TYPES:  Storage for CDEV - All permanent CDEV storage must be referred to by a single
  70.  *            handle, so put all permanent variables in a CDEVRec.
  71.  */
  72. typedef struct CDEVRec
  73. {
  74.     Boolean needUpdate;
  75. } CDEVRec, *CDEVPtr, **CDEVHnd;
  76.  
  77.  
  78. /*
  79.  *  PROTOTYPES
  80.  */
  81. void DoEditCommand (short message, DialogPtr CPDialog);
  82. pascal CDEVHnd main(short message, short item, short numItems, short CPanelID,
  83.          EventRecord *theEvent, CDEVHnd cdevStorage, DialogPtr CPDialog);
  84. void UpdateCharCount(DialogPtr CPDialog);
  85.  
  86.  
  87. /* This is the main dispatcher. It must be the first code in the cdev.
  88.  * EditCdev's dispatcher responds only to the following messages from
  89.  * the Control Panel:
  90.  *    
  91.  *        macDev        - To indicate what machines it is available on.
  92.  *        nulDev        - To check if the character count items need to be updated.
  93.  *        initDev        - To set up some temporary storage and get the caret started.
  94.  *        keyEvtDev    - To check for an edit command and do the appropriate action.
  95.  *        cutDev        - To cut the current selection.
  96.  *        copyDev        - To copy the current selection.
  97.  *        pasteDev    - To paste the contents of the clipboard.
  98.  *        clearDev    - To delete the current selection.
  99.  *
  100.  * The Dialog Manager's services are used to handle entry of text, selection
  101.  * of text, editing of text, and moving between the editText items via the
  102.  * tab key. Since the Dialog Manager handles the selection of text, we do not
  103.  * have to be concerned with hitDev messages for the editText items. The only
  104.  * things we have to take care of are calling the Dialog Manager editing
  105.  * routines in response to an edit command, and getting the caret to show up
  106.  * at the beginning. In response to an edit command that was the result of
  107.  * a command-key equivalent, we must also eliminate the event so that it does
  108.  * not get processed as a keyDown by the Dialog Manager. Otherwise, an 'x'
  109.  * would show up in the editText item when the user did a command-x to cut
  110.  * the text.
  111.  *        Also, we must be aware that the dialog manager does not keep track
  112.  * of the length of the characters typed or pasted into the textEdit items.
  113.  * Since the maximum number of characters that may be contained in a single
  114.  * textEdit item is 32K, we must make sure that the user does not enter more
  115.  * than some maximum number of characters.
  116.  */
  117.  
  118.  
  119. pascal CDEVHnd main(short message, short item, short numItems, short CPanelID,
  120.                        EventRecord *theEvent, CDEVHnd cdevStorage, DialogPtr CPDialog)
  121. {
  122. #pragma unused (item, CPanelID)        /* unused formal parameters */
  123.  
  124.     char         tempChar;
  125.     TEHandle    myTEHandle;    /* handle to the current textEdit record */
  126.     short        selLength;    /* length of current selection */
  127.  
  128.     if (message == macDev) return((CDEVHnd) 1);                /* we work on every machine */
  129.     else if (cdevStorage != nil) {
  130.         switch (message) {
  131.             case initDev:                                    /* initialize cdev */
  132.                 cdevStorage = (CDEVHnd)NewHandle(sizeof(CDEVRec));    /* create private storage */
  133.                 SelectDialogItemText(CPDialog, numItems + textItm, 0, 999); /* make caret show up */
  134.                 break;
  135.                 
  136.             case nulDev:
  137.                 if ((**cdevStorage).needUpdate)        /* character count need updating? */
  138.                 {
  139.                     UpdateCharCount(CPDialog);
  140.                     (**cdevStorage).needUpdate = false;
  141.                 }
  142.                 break;
  143.  
  144.             case hitDev:                                    /* handle hit on item */
  145.             case closeDev:                                    /* clean up and dispose */
  146.             case updateDev:                                    /* handle any update drawing */
  147.             case activDev:                                    /* activate any needed items */
  148.             case deactivDev:                                /* deactivate any needed items */
  149.                 break;
  150.                 
  151.             case keyEvtDev:                                    /* respond to keydown */
  152.                 tempChar = theEvent->message & charCodeMask;/* get the character, and check */
  153.                 if (theEvent->modifiers & cmdKey)            /*  status of command key */
  154.                 {
  155.                     message = nulDev;                        /* start with no message */
  156.                     theEvent->what = nullEvent;                /* and empty event type */
  157.                     
  158.                     switch (tempChar)                        /* set appropriate message */
  159.                     {    
  160.                         case 'X':
  161.                         case 'x':
  162.                             message = cutDev;
  163.                             break;
  164.                         case 'C':
  165.                         case 'c':
  166.                             message = copyDev;
  167.                             break;
  168.                         case 'V':
  169.                         case 'v':
  170.                             message = pasteDev;
  171.                             break;
  172.                     }
  173.                     DoEditCommand(message, CPDialog);        /* Let edit command handler take it */
  174.                 }
  175.                 else    /* not a command key */
  176.                 {
  177.                     /*   We need to update the character count, but only after textEdit has    */
  178.                     /* a chance to deal with it.  Set a flag so that during the next nullEvent */
  179.                     /* we will process the character count update.  If we call UpdateCharCount */
  180.                     /* now, we will display the incorrect count.                               */
  181.                     /*   Also, we must make sure that we are not exceeding our character limit */
  182.                     /* with this character.  (unless it is a tab or backspace, that's OK)       */
  183.                     
  184.                     (**cdevStorage).needUpdate = true;        /* update count during next nulDev */
  185.                     
  186.                     myTEHandle = ((DialogPeek)CPDialog)->textH;
  187.                     selLength = ((**myTEHandle).selEnd-(**myTEHandle).selStart);
  188.  
  189.                     if ( ((**myTEHandle).teLength>=maxLength+selLength) &&
  190.                             (tempChar!=delete) && (tempChar!=tab) &&
  191.                             (tempChar!=leftArrow) && (tempChar!=rightArrow) &&
  192.                             (tempChar!=upArrow) && (tempChar!=downArrow) )
  193.                     {
  194.                         theEvent->what = nullEvent;    /* trash character (textEdit never sees it) */
  195.                         SysBeep(1);
  196.                     }
  197.                 }
  198.                 break;
  199.                 
  200.             case macDev:
  201.             case undoDev:
  202.                 break;
  203.                 
  204.             case cutDev:
  205.             case copyDev:
  206.             case pasteDev:
  207.             case clearDev:
  208.                 DoEditCommand(message, CPDialog);        /* respond to edit command */
  209.                 break;
  210.         }
  211.  
  212.         return (cdevStorage);
  213.     }  /* cdevStorage != nil */
  214.     
  215.     return (nil);    /* if cdevStorage = NIL then ControlPanel will put up memory error. */
  216. }
  217.  
  218. /* Call the appropriate Dialog Manager routine to handle an edit command for */
  219. /* an editText item. It will do *ALMOST* all the work regarding the TEScrap. */
  220. void DoEditCommand(short message, DialogPtr CPDialog)
  221. {
  222.     TEHandle    myTEHandle;        /* Handle to current textEdit record */
  223.     long        scrapLength;    /* length of textEdit scrap */
  224.     long        freeSpace;        /* amount of space before character limit */
  225.     short        selLength;        /* length of current selection */
  226.  
  227.     myTEHandle = ((DialogPeek)CPDialog)->textH;
  228.  
  229.     switch (message)
  230.     {
  231.     case cutDev:
  232.         DialogCut(CPDialog);
  233.         break;
  234.     case copyDev:
  235.         DialogCopy(CPDialog);
  236.         break;
  237.     case pasteDev:
  238.         /* Since the maximum number of characters that a textEdit structure can   */
  239.         /* handle is 32K, and the dialog manager doesn't check for overflow when  */
  240.         /* pasting, we must check for ourselves and only paste as many characters */
  241.         /* as we have room for. */
  242.         scrapLength = TEGetScrapLength();
  243.         selLength = ((**myTEHandle).selEnd-(**myTEHandle).selStart);
  244.         freeSpace = (long)(maxLength-(**myTEHandle).teLength)+selLength;
  245.         if (freeSpace>=scrapLength)    /* enough room for paste? */
  246.             DialogPaste(CPDialog);
  247.         else                        /* not enough room for paste */
  248.         {
  249.             SysBeep(1);
  250.             if (freeSpace>0)        /* any room for paste? */
  251.             {
  252.                 TESetScrapLength(freeSpace);    /* only paste as many characters as free */
  253.                 DialogPaste(CPDialog);
  254.                 TESetScrapLength(scrapLength);    /* restore textEdit scrap length */
  255.             }
  256.         }
  257.         break;
  258.     case clearDev:
  259.         DialogDelete(CPDialog);
  260.         break;
  261.     }
  262.     UpdateCharCount(CPDialog);
  263. }
  264.  
  265. /* Examine the appropriate structures to determine the number of characters in the */
  266. /* acvive TERecord and display in the corresponding static text item. */
  267. void UpdateCharCount(DialogPtr CPDialog)
  268. {
  269.     TEHandle    myTEHandle;
  270.     short        itemType;
  271.     Handle        itemHandle;
  272.     Rect        itemRect;
  273.     Str255        text;
  274.     short        editItem;
  275.  
  276.     myTEHandle = ((DialogPeek)CPDialog)->textH;            /* get handle to TERecord */
  277.     HLock((Handle)myTEHandle);
  278.     NumToString((long)((**myTEHandle).teLength), text);    /* get length of text */
  279.     HUnlock((Handle)myTEHandle);
  280.     editItem=countItm+((DialogPeek)CPDialog)->editField; /* calculate which item to change */
  281.     GetDialogItem(CPDialog, editItem, &itemType, &itemHandle, &itemRect);
  282.     SetDialogItemText(itemHandle, text);                            /* change text item contents */
  283. }
  284.